package com.example.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.api.RabbitListenerErrorHandler;
import org.springframework.amqp.rabbit.retry.RejectAndDontRequeueRecoverer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;

import static com.example.entity.constant.mq.RabbitmqConstant.*;


@Configuration
//@ConditionalOnClass(RabbitTemplate.class)
public class RabbitConfig {
    @Bean //jdk序列化
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

    // 配置消费重试拦截器（重试3次后拒绝）
    @Bean
    public RetryOperationsInterceptor retryInterceptor() {
        return RetryInterceptorBuilder.stateless()
                .maxAttempts(3) // 最大重试次数：1次正常消费 + 3次重试 = 共4次
                .backOffOptions(1000, 2.0, 5000) // 退避策略：初始1秒，每次倍增，最大5秒
                .recoverer(new RejectAndDontRequeueRecoverer()) // 重试耗尽后：拒绝且不重回队列
                .build();
    }
    // 配置消费重试拦截工厂
    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setAdviceChain(retryInterceptor()); // 关联重试拦截器
        return factory;
    }

    // 可选：自定义异常处理（记录失败日志）
    @Bean
    public RabbitListenerErrorHandler seckillErrorHandler() {
        return (amqpMessage, message, exception) -> {
            System.err.println("秒杀消息消费失败！消息内容：" + new String(amqpMessage.getBody())
                    + "，异常：" + exception.getMessage());
            // 抛出异常，让RetryInterceptor触发拒绝逻辑
            throw new RuntimeException("消费重试耗尽，拒绝消息", exception);
        };
    }



    // -------------------------------------------------------------------------

    /**
     * 定义秒杀队列（主队列）
     * 特点：持久化、非独占、不自动删除
     */
    @Bean
    public Queue seckillQueue() {
        // durable(true)：队列持久化（服务重启后不丢失）（默认）
        // exclusive(false)：非独占（多个连接可访问）（默认）
        // autoDelete(false)：不自动删除（队列无消费者也不删除）（默认）
        return QueueBuilder.durable(SECKILL_QUEUE)
                // 绑定死信交换机：消息成为死信后转发到该交换机
                .withArgument("x-dead-letter-exchange", SECKILL_DL_EXCHANGE)
                // 绑定死信路由键：死信消息使用该路由键转发
                .withArgument("x-dead-letter-routing-key", SECKILL_DL_ROUTING_KEY)
                // 可选：设置队列最大长度（超出后最旧消息成为死信）
                .withArgument("x-max-length", 10000)
                .build();
    }
    /**
     * 定义秒杀交换机（直连型）
     * 特点：按路由键精确匹配，适合秒杀等需要精准路由的场景
     */
    @Bean
    public DirectExchange seckillExchange() {
        // durable(true)：交换机持久化
        return new DirectExchange(SECKILL_EXCHANGE, true, false);
    }
    /**
     * 绑定队列与交换机
     * 规则：秒杀交换机 + 秒杀路由键 → 秒杀队列
     */
    @Bean
    public Binding seckillBinding() {
        // 将队列通过路由键绑定到交换机
        return BindingBuilder.bind(seckillQueue())
                .to(seckillExchange())
                .with(SECKILL_ROUTING_KEY);
    }

    // -------------------------------------------------------------------------

    /**
     * 死信队列（存储消费失败的秒杀请求）
     */
    @Bean
    public Queue seckillDlQueue() {
        // 死信队列通常需要持久化，用于后续人工处理或重试
        return QueueBuilder.durable(SECKILL_DL_QUEUE).build();
    }
    /**
     * 死信交换机（直连型）
     */
    @Bean
    public DirectExchange seckillDlExchange() {
        return new DirectExchange(SECKILL_DL_EXCHANGE, true, false);
    }
    /**
     * 绑定死信队列与死信交换机
     */
    @Bean
    public Binding seckillDlBinding() {
        return BindingBuilder.bind(seckillDlQueue())
                .to(seckillDlExchange())
                .with(SECKILL_DL_ROUTING_KEY);
    }
}
